﻿using System;
using System.Linq;
using SubSonic.Linq.Structure;
using SubSonic.DataProviders;
using System.Collections.Generic;




using Catalyst.Caching;
using System.IO;
using System.Linq.Expressions;
using SubSonic.Schema;
using System.Web.Caching;
using System.Data;
using SubSonic.Query;
using System.Collections;
using System.Reflection;
using SubSonic.Extensions;

namespace Catalyst.Linq
{
	public class CacheQueryProvider : DbQueryProvider, IQueryProvider
	{
		private readonly IDataProvider _provider;
		private string tableName;
		private Expression _expression;
		private string databaseName;

		public CacheQueryProvider(IDataProvider provider, string DatabaseName)
			: base(provider)
		{
			_provider = provider;
			databaseName = DatabaseName;
		}

		#region IQueryProvider Members

		IQueryable<S> IQueryProvider.CreateQuery<S>(Expression expression)
		{
			return new Catalyst.Linq.Query<S>(this, expression);
		}

		IQueryable IQueryProvider.CreateQuery(Expression expression)
		{
			Type elementType = TypeHelper.GetElementType(expression.Type);
			try
			{
				return (IQueryable)Activator.CreateInstance(typeof(Catalyst.Linq.Query<>).MakeGenericType(elementType), new object[] { this, expression });
			}
			catch (TargetInvocationException tie)
			{
				throw tie.InnerException;
			}
		}

		//S IQueryProvider.Execute<S>(Expression expression)
		//{
		//    Type elementType = TypeHelper.GetElementType(expression.Type);
		//    ITable table = Mapping.Language.DataProvider.FindTable(elementType.Name);
		//    tableName = table.Name;

		//    bool IsEnumerable = (typeof(S).Name == "IEnumerable`1");

		//    return (S)CacheQueryContext.Execute(expression, IsEnumerable, _provider, elementType);
		//}

		S IQueryProvider.Execute<S>(Expression expression)
		{
			Type elementType = TypeHelper.GetElementType(expression.Type);
		    ITable table = Mapping.Language.DataProvider.FindTable(elementType.Name);
		    tableName = table.Name;

			//_expression = expression;
		    if (checkIfCachedTable())
		    {
				MethodCallExpression mce = expression as MethodCallExpression;

				if (mce != null)
		        {
		            _expression = getPrimaryTableExpression(mce);
		        }
		        else
		        {
		            _expression = expression;
		        }

		        IEnumerable allItems = CachingService.GetCachedObject<IEnumerable>(getCacheKey(), new CachingService.ObjectLoader(getAllCachedData));

		        //S clonedItems = Cloning.CloneHelper<S>.Clone(allItems);

		        if (mce != null && mce.Arguments.Count > 1)
		        {
		            //ParameterExpression p = Expression.Parameter(elementType, "allItems");

		            //LambdaExpression lambda = Expression.Lambda(mce.Arguments[1], p);
		            //var x = lambda.Compile();

		            //Type lambdaType = typeof(Func<,>).MakeGenericType(elementType, typeof(bool));

		            //var predicate = Expression.Lambda(lambdaType, mce.Arguments[1], lambda.Parameters);

		        //    return clonedItems;
		        }

		        return (S)allItems;
		    }
		    else
		    {
		       return (S)base.Execute(expression);
		    }
		}

		#endregion

		public override object Execute(Expression expression)
		{
			return base.Execute(expression);
		}

		private Expression getPrimaryTableExpression(MethodCallExpression mce)
		{
			foreach (Expression e in mce.Arguments)
			{
				MethodCallExpression m = e as MethodCallExpression;

				if (m != null)
				{
					return getPrimaryTableExpression(m);
				}

				return e;
			}

			return mce.Arguments[0];
		}

		private string getCacheKey()
		{
			return String.Format("{0}_{1}", databaseName, tableName);
		}

		private CacheSettings getAllCachedData()
		{
			CacheSettings settings = new CacheSettings();

			SqlCacheDependency dependency = null;

			if (checkIfCachedTable())
			{
				// Change ProviderName to DatabaseName
				dependency = new SqlCacheDependency(databaseName, tableName);
			}

			settings.CachedObject = base.Execute(_expression);
			settings.Dependency = dependency;

			return settings;
		}

		    private bool checkIfCachedTable()
		    {
		        //if (HttpContext.Current != null)
		        //{
		            foreach (string enabledTable in SqlCacheEnforcer.GetTablesEnabledForSqlCacheDependency(_provider.ConnectionString))
		            {
		                if (enabledTable == tableName)
		                {
		                    return true;
		                }
		            }
		        //}

		        return false;
		    }


	}






	


	






	//internal class LocationFinder : ExpressionVisitor
	//{
	//    private Expression expression;
	//    private List<string> locations;

	//    public LocationFinder(Expression exp)
	//    {
	//        this.expression = exp;
	//    }

	//    public List<string> Locations
	//    {
	//        get
	//        {
	//            if (locations == null)
	//            {
	//                locations = new List<string>();
	//                this.Visit(this.expression);
	//            }
	//            return this.locations;
	//        }
	//    }

	//    protected override Expression VisitBinary(BinaryExpression be)
	//    {
	//        if (be.NodeType == ExpressionType.Equal)
	//        {
	//            if (ExpressionTreeHelpers.IsMemberEqualsValueExpression(be, typeof(Place), "Name"))
	//            {
	//                locations.Add(ExpressionTreeHelpers.GetValueFromEqualsExpression(be, typeof(Place), "Name"));
	//                return be;
	//            }
	//            else if (ExpressionTreeHelpers.IsMemberEqualsValueExpression(be, typeof(Place), "State"))
	//            {
	//                locations.Add(ExpressionTreeHelpers.GetValueFromEqualsExpression(be, typeof(Place), "State"));
	//                return be;
	//            }
	//            else
	//                return base.VisitBinary(be);
	//        }
	//        else
	//            return base.VisitBinary(be);
	//    }
	//}



	



	
}